from Common import Common
import re
from typing import Dict, List, Mapping
from MenuItem import MenuItem
import os


class Menu(Common):
    '菜单'
    numListOfHotkeyMenu: Mapping[str, int] = {}

    def __init__(self, data: str = '', content: str | List[str] = '', level: int = 0, parentMenu: 'Menu' = None, notes: List[str] = [], isHotkeyMenu=False):
        super().__init__(data, parentMenu, notes)
        self.content = content
        '菜单内容'
        self.subMenu: List[Menu] = []
        '子菜单'
        self.level = level
        '菜单等级'
        self.menuItems: List[MenuItem] = []
        '菜单项'
        self.isHotkeyMenu: bool = isHotkeyMenu
        '是快捷键菜单'
        self.numKeyList: List[bool] = []
        '记录当前菜单下所使用的数字快捷键'

        try:
            if len(self.data) > 0 and len(self.content) > 0:
                self._parseMenu()
            elif len(self.content) > 0:
                self._parseRoot()
        except:
            raise

    def _parseRoot(self):
        '解析根菜单'
        assert self.content != '', '根菜单内容不能为空'

        self.content = self.content.split('\n')
        self._parse()

    def _parseMenu(self):
        '解析菜单'

        m = re.match('-+(.*)\(&(.*)\)', self.data)
        if m == None:
            m = re.match('-+(.*)', self.data)
            if m == None:
                raise Exception('无法解析：%s' % self.data)
            else:
                self.name = m.group(1)
                self.wordsOfName = self._parseNameWords(self.name)
        else:
            self.name = m.group(1)
            self.wordsOfName = self._parseNameWords(m.group(2))

        assert self.name != '', '菜单名不能为空：%s' % self.data

        assert len(self.wordsOfName) > 0, '菜单名不能为空：%s' % self.data

        assert len(self.content) != 0, '菜单内容不能为空：%s, %s' % (
            self.data, self.content)

        self._parse()

    def _parse(self):
        '解析'
        findFirstMenu = False
        '已找到第一个子菜单'
        subMenuData: str = ''
        '子菜单数据'
        subMenuContent: List[str] = []
        '子菜单内容'
        notesWithoutAttribution: List[str] = []
        '没有归属的注释'
        notesOfSubMenu: List[str] = []
        '子菜单的注释'

        for line in self.content:
            line = line.strip()
            if line.__len__() == 0:
                continue

            # 是菜单
            if self._isMenu(line):
                level = self._calMenuLevel(line)

                # 是子菜单
                if self.level + 1 == level:
                    # 处理前一个子菜单
                    if findFirstMenu:
                        try:
                            subMenu = Menu(subMenuData, subMenuContent,
                                           self.level+1, self, notesOfSubMenu)
                            self.subMenu.append(subMenu)
                            subMenuContent.clear()
                            notesOfSubMenu = []
                        except:
                            raise

                    findFirstMenu = True
                    subMenuData = line
                    if notesWithoutAttribution.__len__() > 0:
                        notesOfSubMenu = notesWithoutAttribution
                        notesWithoutAttribution = []
                else:
                    if notesWithoutAttribution.__len__() > 0:
                        subMenuContent.extend(notesWithoutAttribution)
                        notesWithoutAttribution = []
                    subMenuContent.append(line)
            # 是注释
            elif self._isNoteLine(line):
                notesWithoutAttribution.append(line)
            # 子菜单的内容
            elif findFirstMenu:
                if notesWithoutAttribution.__len__() > 0:
                    subMenuContent.extend(notesWithoutAttribution)
                    notesWithoutAttribution = []
                subMenuContent.append(line)
            # 当前菜单的菜单项
            else:
                try:
                    item = MenuItem(line, self, notesWithoutAttribution)
                    notesWithoutAttribution = []
                    self.menuItems.append(item)
                except:
                    raise

        # 处理最后一个子菜单
        if subMenuData != '':
            if subMenuContent.__len__() > 0:
                try:
                    subMenu = Menu(subMenuData, subMenuContent,
                                   self.level+1, self, notesOfSubMenu)
                    self.subMenu.append(subMenu)
                except:
                    raise
            else:
                raise Exception('存在空菜单：%s' % subMenuData)

    def _isMenu(self, str) -> bool:
        '是菜单'
        if len(str) == 0:
            return False
        return str[0] == '-'

    def _calMenuLevel(self, str) -> int:
        menuLevel = 0
        i = 0
        while i < len(str):
            if(str[i] == '-'):
                menuLevel += 1
                i += 1
            else:
                break
        return menuLevel

    def checkMenuHotkey(self) -> str:
        '检查同级菜单快捷键是否有重复的'
        errorInfo: str = ''
        exits: Dict[str, List[Menu]] = {}

        for menu in self.subMenu:
            if menu.getMenuKey() in exits:
                exits[menu.getMenuKey()].append(menu)
            else:
                exits[menu.getMenuKey()] = [menu]

        for k in exits.keys():
            arr = exits[k]
            if len(arr) > 1:
                errorInfo = '重复快捷键[%s]的菜单有：\n'
                for menu in arr:
                    errorInfo += menu._getNamePath() + '\n'

        for menu in self.subMenu:
            errorInfo += menu.checkMenuHotkey()

        return errorInfo

    def checkExeExist(self) -> str:
        '检查配置中所有软件路径是否存在'
        errorInfo = ''

        for item in self.menuItems:
            if not item.isExe():
                continue

            if not os.path.exists(self._handleEnvironOfPath(item.content)):
                errorInfo += item.data + '\n'

        for menu in self.subMenu:
            errorInfo += menu.checkExeExist()

        return errorInfo

    def getMenuKey(self) -> str:
        '获得菜单快捷键'
        return self.wordsOfName[0][0]

    def generateHotkeyMenu(self):
        '生成快捷键菜单'
        itemList = self.menuItems.copy()

        for item in itemList:
            level = item.getCurLevel()

            # 需要用数字进行区分
            if level == item.getHotkeyPathLen():
                item.numToSameName = self.getEnableUsedNumKey()
            else:
                key = item.getHotkey(level)
                if self.existMenuOrMenuItemByKey(key, item):
                    menu = self.getMenuByKey(key)
                    if menu == None:
                        menu = self.createSubMenu(key)
                    menu.menuItems.append(item)
                    self.menuItems.remove(item)
                    item.parentMenu = menu

        for menu in self.subMenu:
            menu.generateHotkeyMenu()

    def getMenuByKey(self, key: str) -> 'Menu':
        '获得指定快捷键的菜单'
        ret = None
        for menu in self.subMenu:
            if menu.wordsOfName.__len__() == 0:
                print
            if menu.getMenuKey() == key:
                ret = menu
                break
        return ret

    def createSubMenu(self, key: str) -> 'Menu':
        '创建子快捷键菜单'
        menu = Menu(level=self.level+1, parentMenu=self, isHotkeyMenu=True)
        menu.name = key
        menu.wordsOfName.append(key)
        self.subMenu.append(menu)
        return menu

    def getEnableUsedNumKey(self) -> int:
        '获得一个可用的数字快捷键'
        num = self.numKeyList.__len__()
        count = 0
        while self.existMenuOrMenuItemByKey(num.__str__()):
            self.numKeyList.append(True)
            num = self.numKeyList.__len__()
            count += 1
            if count > 100:
                raise Exception('循环超时')

        self.numKeyList.append(True)
        return num

    def existMenuOrMenuItemByKey(self, key: str, exclude=None) -> bool:
        '是否存在同样快捷键的菜单或菜单项'
        ret = False
        for item in self.menuItems:
            if item == exclude:
                continue

            level = item.getCurLevel()
            if level == item.getHotkeyPathLen():
                if item.numToSameName.__str__() == key:
                    ret = True
                    break
                else:
                    continue
            elif item.getHotkey(level) == key:
                ret = True
                break

        if ret:
            return True

        for menu in self.subMenu:
            if menu == exclude:
                continue

            if menu.wordsOfName[0][0] == key:
                ret = True
                break

        return ret

    def sort(self):
        '排序'
        if self.subMenu.__len__() > 0 and self.subMenu[0].isHotkeyMenu:
            self.subMenu.sort(key=lambda menu: menu.getFinalHotkey())

        for menu in self.subMenu:
            menu.sort()

    def generateIni(self) -> str:
        ret = ''

        # 是根菜单
        if self.isRoot():
            for item in self.menuItems:
                ret += '%s\n' % item.getIni()
            for menu in self.subMenu:
                ret += menu.generateIni()
        else:
            # 菜单自己
            ret += self.__getIni()

            # 菜单项
            for item in self.menuItems:
                ret += item.getIni()

            # 子菜单
            for menu in self.subMenu:
                ret += menu.generateIni()

        return ret

    def getFinalHotkey(self) -> str:
        '获得最终快捷键'
        return self.wordsOfName[0][0]

    def getFinalName(self) -> str:
        if self.isHotkeyMenu:
            return self.name.upper()+self.hotkeyNum.__str__()
        else:
            return self.name

    def isRoot(self) -> bool:
        '是根菜单'
        return self.data.__len__() == 0 and not self.isHotkeyMenu

    def numberingMenus(self):
        '对菜单进行编号'
        if self.isHotkeyMenu:
            if self.name in self.numListOfHotkeyMenu.keys():
                self.hotkeyNum = self.numListOfHotkeyMenu[self.name]+1
                self.numListOfHotkeyMenu[self.name] = self.hotkeyNum
            else:
                self.hotkeyNum = 1
                self.numListOfHotkeyMenu[self.name] = 1

        for menu in self.subMenu:
            menu.numberingMenus()

    def optimizeMenuPath(self):
        '优化菜单路径'
        c = 0
        while self.menuItems.__len__() == 0 and self.subMenu.__len__() == 1:
            menu = self.subMenu[0]
            self.subMenu = menu.subMenu
            self.menuItems = menu.menuItems
            for menu in self.subMenu:
                menu.parentMenu = self
            for item in self.menuItems:
                item.parentMenu = self

            c += 1
            if c > 100:
                raise Exception('超时：%s' % self.data)

        for menu in self.subMenu:
            menu.optimizeMenuPath()

    def _isNoteLine(self, line: str) -> bool:
        '是注释行'
        return line.__len__() > 0 and line[0] == ';'

    def __getIni(self) -> str:
        commit = self._getNotesIni()
        indent = '\t'*(self.level-1)
        menuIdentifier = '-' * self.level
        name = self.getFinalName()
        key = self.getFinalHotkey().upper()
        return '%s%s%s%s(&%s)\n' % (commit, indent, menuIdentifier, name, key)
